package main

import (
	"encoding/json"
	"fmt"
	"github.com/go-ping/ping"
	"github.com/robfig/cron/v3"
	log "github.com/sirupsen/logrus"
	"gopkg.in/ini.v1"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"time"
)

//cookie 内容
type Cookie struct {
	Session  string `json:"SESSION"`
	Token    string `json:"token"`
	Username string `json:"username"`
}

//获取打卡状态 接口返回消息结构
type Msg struct {
	Code float64 `json:"code"`
	Data struct {
		Datalist []struct {
			Status string `json:"status"`
			Time   string `json:"time"`
		} `json:"datalist"`
		EarlyNum  float64 `json:"earlyNum"`
		LateNum   float64 `json:"lateNum"`
		LeaveNum  float64 `json:"leaveNum"`
		NoSignNum float64 `json:"noSignNum"`
	} `json:"data"`
	Message string `json:"message"`
}

//配置
type Config struct {
	OffWorkTime int    `ini:"off_work_time"` //下班时间,小时
	Ip          string `ini:"ip"`            //手机ip
	PingCount   int    `ini:"pingCount"`     //ping 手机ip 次数
	Interval    string `ini:"interval"`      //程序间隔检查时间
	Port        string `ini:"port"`          //本程序网络端口
	InfoUrl     string `ini:"info_url"`      //获取打卡信息url
	CallUrl     string `ini:"call_url"`      //发送电话通知url
}

var cookie Cookie
var config Config

func init() {
	fmt.Println("打卡提醒程序正在运行，请不要关闭")
	//配置日志输出到console,同时写文件
	writer1 := os.Stdout
	writer2, _ := os.OpenFile(getRealPath("clock_reminder.log"), os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0755)
	log.SetFormatter(&log.JSONFormatter{})
	log.SetOutput(io.MultiWriter(writer1, writer2))
	log.SetLevel(log.DebugLevel)

	//初始化配置
	conf, err := ini.Load(getRealPath("config.ini"))
	if err != nil {
		log.Error("读取配置文件 config.ini 失败")
	}
	_ = conf.MapTo(&config)
}

func main() {
	// 也可以使用 key => value 格式
	crontab := cron.New()
	// 添加定时任务, 每20秒执行一次
	crontab.AddFunc("@every "+config.Interval+"s", handlerTask)
	// 启动定时器
	crontab.Start()
	//  http服务，接收cookie信息
	http.HandleFunc("/", handlerHttp)
	http.ListenAndServe(":"+config.Port, nil)
}

//http服务，接收cookie信息
func handlerHttp(w http.ResponseWriter, r *http.Request) {
	decoder := json.NewDecoder(r.Body)
	if decoder.Decode(&cookie) != nil {
		fmt.Fprint(w, "参数解码失败")
		return
	}
	fmt.Fprint(w, cookie)
}

/**
定时任务，检查是否下班忘记打卡
*/
func handlerTask() {
	now := time.Now()
	if now.Hour() < config.OffWorkTime {
		log.Info("还没到下班时间，跳过")
		return
	}
	if cookie == (Cookie{}) {
		log.Info("没有cookie，跳过")
		return
	}
	static := pingCheck()
	if static.PacketsRecv > 0 {
		log.Info("手机网络正常，还没走人，跳过")
		return
	}
	//获取打卡信息
	url := config.InfoUrl + "?current=%s" + now.Format("2006-01")
	log.Info("获取打卡信息:" + url)
	client := &http.Client{}
	req, _ := http.NewRequest("GET", url, nil)
	req.Header.Add("Content-Type", "application/json")
	req.Header.Add("Cookie", "SESSION="+cookie.Session+
		";username="+cookie.Username+";JSESSIONID="+cookie.Token)
	//处理返回结果
	response, _ := client.Do(req)
	//关闭流
	defer response.Body.Close()
	//检出结果集
	body, err := ioutil.ReadAll(response.Body)
	if err != nil {
		log.Error("获取打卡信息失败，%s\n", err.Error())
		return
	}
	var msg Msg
	json.Unmarshal(body, &msg)
	if msg.Message != "操作成功" {
		log.Info("获取打卡信息失败, 接口返回错误")
		log.Info(msg)
		return
	}
	//重置cookie，预防重复提醒
	cookie = Cookie{}
	//发送提醒
	firstData := msg.Data.Datalist[0]
	if firstData.Status == "缺卡" && firstData.Time == now.Format("2006-01-02") {
		//发送提醒
		log.Info("你好像还没打卡，请确认")
		http.Get(config.CallUrl)
	}
}

//pingCheck 手机ip
func pingCheck() *ping.Statistics {
	log.Debug("正在ping")
	pinger, err := ping.NewPinger(config.Ip)
	pinger.SetPrivileged(true)
	pinger.Timeout = time.Second * 10
	if err != nil {
		panic(err)
	}
	pinger.Count = config.PingCount
	err = pinger.Run() // Blocks until finished.
	if err != nil {
		panic(err)
	}
	return pinger.Statistics()
}

//获取配置文件的真实路径，文件在当前文件夹下存在，则返回，不存在则使用当前exe绝对路径
func getRealPath(fileName string) string {
	_, err := os.Stat("./" + fileName)
	if err == nil {
		return "./" + fileName
	}
	file, _ := exec.LookPath(os.Args[0])
	path, _ := filepath.Abs(file)
	return filepath.Dir(path) + string(os.PathSeparator) + fileName
}
